package com.sikulifang.bank.simulation.server.handler;

import cn.hutool.core.util.StrUtil;
import com.sikulifang.bank.simulation.persistence.Bank;
import com.sikulifang.bank.simulation.service.MessageService;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.DefaultFullHttpResponse;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.HttpHeaderNames;
import io.netty.handler.codec.http.HttpHeaderValues;
import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.handler.codec.http.HttpUtil;
import io.netty.handler.codec.http.HttpVersion;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationContext;

import java.net.URLDecoder;

/**
 * 业务代理 - 抽象类
 * 代理处理器抽象类，用于抽取正向代理及反向代理功能
 *
 * @author zhoul
 */
@Slf4j
public class HttpHandler extends SimpleChannelInboundHandler<FullHttpRequest> {

    protected FullHttpRequest request;
    @Setter
    @Getter
    private Bank bank;
    @Setter
    @Getter
    private ApplicationContext applicationContext;

    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) {
        ctx.flush();
    }


    @Override
    public void channelRead0(final ChannelHandlerContext ctx, FullHttpRequest request) throws Exception {
        if (HttpUtil.is100ContinueExpected(request)) {
            ctx.write(new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.CONTINUE));
        }
        this.request = request;
        if (StrUtil.isNotBlank(bank.getUrl())) {
            String uri = request.uri();
            if (!StrUtil.equalsIgnoreCase(uri, "/" + bank.getUrl())) {
                log.error("当前请求URL'{}'错误----,配置的URL为：'{}'", uri, bank.getUrl());
                byte[] bytes = StrUtil.bytes("当前请求URL" + uri + "错误", bank.getCharset());
                httpFlush(ctx, HttpUtil.isKeepAlive(request), bytes);
                return;
            }
        }
        String contentType = request.headers().get(HttpHeaderNames.CONTENT_TYPE);

        String charset = bank.getCharset();
        byte[] buf = new byte[request.content().readableBytes()];
        request.content().readBytes(buf);
        String reqString;
        if (HttpHeaderValues.APPLICATION_X_WWW_FORM_URLENCODED.toString().equalsIgnoreCase(contentType)) {
            reqString = URLDecoder.decode(new String(buf, charset), charset);
        } else {
            reqString = new String(buf, charset);
        }

        ctx.channel().eventLoop().execute(() -> {
            try {
                handler(ctx, reqString);
            } catch (Exception e) {
                e.printStackTrace();
            }
        });
    }

    private void handler(ChannelHandlerContext ctx, String reqString) {
        MessageService bankService = getApplicationContext().getBean(MessageService.class);
        byte[] resultBytes = bankService.handleMessage(bank, reqString);

        httpFlush(ctx, HttpUtil.isKeepAlive(request), resultBytes);

    }

    /**
     * 返回响应
     *
     * @param ctx         上下文
     * @param isKeepAlive 是否为持久链接
     * @param bytes       响应数据
     */
    private void httpFlush(ChannelHandlerContext ctx, boolean isKeepAlive, byte[] bytes) {
        DefaultFullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK);

        if (isKeepAlive) {
            response.headers().set(HttpHeaderNames.CONNECTION, HttpHeaderValues.KEEP_ALIVE);
        }

        response.headers().set("Strict-Transport-Security", "max-age=31536000; includeSubDomains");
        // response.headers().set(HttpHeaderNames.CONTENT_TYPE, "text/html; charset=UTF-8");
        if (null != bytes) {
            HttpUtil.setContentLength(response, bytes.length);
        }

        try {
            ByteBuf content = response.content();
            content.writeBytes(bytes);
        } catch (Exception e) {
            e.printStackTrace();
        }

        ChannelFuture future = ctx.writeAndFlush(response);
        if (!isKeepAlive) {
            future.addListener(ChannelFutureListener.CLOSE);
        }
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
        ctx.close();
    }

}


